#include "av_frame_buf.h"
#include "ffvdp_ring.h"

#include <stdio.h>

struct desc_priv_s {
#define FRM_BUF_RING_SIZE 64
    ffvdp_ring_t *frame_r;
    char *frame_buffer;
};

struct priv_hdr_s {
    char *pos;
    size_t buf_left;
};

static av_frame_buf_manager_t *_frame_buf_alloc_manager(
    av_frame_buf_desc_t *d) {
    assert(d->type == AV_FRAME_BUF_TYPE_GENERIC);

    av_frame_buf_manager_t *m;
    m = (av_frame_buf_manager_t *)calloc(
        1, sizeof(av_frame_buf_manager_t) + d->priv_size);

    struct desc_priv_s *p = (struct desc_priv_s *)(m + 1);
    m->priv = p;
    m->desc = d;

    p->frame_r = ffvdp_ring_alloc(FRM_BUF_RING_SIZE);
    if (!p->frame_r) {
        goto init_error;
    }

    p->frame_buffer = (char *)malloc(FRM_BUF_RING_SIZE * d->max_frame_len);
    if (!p->frame_buffer) {
        goto init_error;
    }
    int i;
    char *tmp;
    for (i = 0; i < FRM_BUF_RING_SIZE; ++i) {
        tmp = p->frame_buffer + i * d->max_frame_len;
        ffvdp_ring_enqueue(p->frame_r, tmp);
    }

    return m;

init_error:
    if (p->frame_buffer) {
        free(p->frame_buffer);
    }

    if (p->frame_r) {
        ffvdp_ring_free(p->frame_r);
    }

    free(m);

    return NULL;
}

static av_frame_buf_t *_frame_buf_alloc(av_frame_buf_manager_t *m) {
    av_frame_buf_t *buf;
    struct desc_priv_s *p;
    p = (struct desc_priv_s *)m->priv;
    if (ffvdp_ring_empty(p->frame_r)) {
        return NULL;
    } else {
        ffvdp_ring_dequeue(p->frame_r, (void **)&buf);
        buf->m = m;
        buf->data =
            (char *)buf + sizeof(av_frame_buf_t) + m->desc->priv_hdr_len;
        struct priv_hdr_s *priv_hdr;
        priv_hdr = (struct priv_hdr_s *)(buf + 1);
        priv_hdr->pos = buf->data;
        buf->data_len = 0;
    }

    return buf;
}

static void _frame_buf_free(av_frame_buf_t *buf) {
    struct desc_priv_s *p;
    p = (struct desc_priv_s *)buf->m->priv;
    ffvdp_ring_enqueue(p->frame_r, buf);
}

static void _frame_buf_reset(av_frame_buf_t *buf) {
    struct priv_hdr_s *priv_hdr;
    buf->data =
        (char *)buf + sizeof(av_frame_buf_t) + buf->m->desc->priv_hdr_len;
    priv_hdr = (struct priv_hdr_s *)(char *)buf + sizeof(av_frame_buf_t);
    priv_hdr->pos = buf->data;
    buf->data_len = 0;
}

static int _frame_buf_add_data(av_frame_buf_t *frm_buf, av_pkt_buf_t *pkt_buf) {
    char *pkt_data;
    size_t pkt_data_len;
    pkt_data = av_pkt_buf_data(pkt_buf);
    pkt_data_len = av_pkt_buf_data_len(pkt_buf);

    struct priv_hdr_s *priv_hdr;
    priv_hdr = (struct priv_hdr_s *)(frm_buf + 1);

    // assert(pkt_data_len <= priv_hdr->buf_left);

    memcpy(priv_hdr->pos, pkt_data, pkt_data_len);
    priv_hdr->pos += pkt_data_len;
    frm_buf->data_len += pkt_data_len;

    // priv_hdr->buf_left -= pkt_data_len;

    return 0;
}

static char *_frame_buf_data(av_frame_buf_t *buf) { return buf->data; }

static size_t _frame_buf_data_len(av_frame_buf_t *buf) { return buf->data_len; }

static av_frame_type_t _frame_buf_type(av_frame_buf_t *buf) {
    (void)buf;
    return AV_FRAME_TYPE_MAX;
}

static int _frame_buf_is_keyframe(av_frame_buf_t *buf) {
    return _frame_buf_type(buf) == AV_FRAME_TYPE_I;
}

av_frame_buf_desc_t g_frame_buf_desc_generic = {
    .type = AV_FRAME_BUF_TYPE_GENERIC,
    .priv_size = sizeof(struct desc_priv_s),
    .priv_hdr_len = sizeof(struct priv_hdr_s),
    .max_frame_len = 500 * 1024,

    .alloc_manager_func = _frame_buf_alloc_manager,
    .alloc_func = _frame_buf_alloc,
    .free_func = _frame_buf_free,
    .reset_func = _frame_buf_reset,
    .add_data_func = _frame_buf_add_data,
    .data_func = _frame_buf_data,
    .data_len_func = _frame_buf_data_len,
    .type_func = _frame_buf_type,
    .is_keyframe_func = _frame_buf_is_keyframe,
};
